produces a note of specified frequency and duration.
The Sound routine generates squarewave tones with frequencies of 280 Hz to 71 kHz and durations of 3.6 ms to 0.92 seconds at 4 MHz. It differs from its PBASIC counterpart in that all frequency values from 1 to 255 produce squarewave tones. PBASIC's Sound instruction generates tones when the frequency is in the range of 1 to 127, and hissing white-noise effects from 128 to 255. Both PBASIC and assembly versions of Sound produce a silent pause with a frequency value of 0.
To use the routine, store a frequency value from 0 to 255 in freq, and a duration from 0 to 255 in duratn. Put the output pin number (0 to 7) into pin and the port number (0 to 2 for RA through RC) into the w register, and call Sound.
The following formula determines the frequency output by Sound (assuming a PIC clock speed of 4 MHz):
The formula shows that freq sets the period of the tone in 14-µs units. As a result, the frequency isn't a linear function of freq. For example, a freq of 1 produces a 280.1-Hz tone, while 2 produces 281.2 Hz. At the higher end of the scale, a freq of 250 produces an 11.9-kHz tone, while 251 produces 14.29 kHz.
PBASIC's Sound command has the same characteristic, but starts slightly below 100 Hz. It has a period resolution of approximately 83µs. If you want to more closely emulate PBASIC, just pad the :loop of this Sound routine to take a total of 83µs.
The PBASIC command permits you to specify a list of frequencies and durations to be played. The demonstration program shows how to do this in assembly by using a pair of lookup tables. If you need white-noise effects in your program, use the Random routine. Call Random from within a tight program loop, and copy one bit of its shift register to the speaker output. The random bit stream will produce a hissing sound.
One hint on using Sound: always assign the port number to w immediately before calling the routine. If you don't, some other instruction that uses w may change its contents, causing an error.
To see Sound in operation, connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble and run SOUND.SRC. When you apply power to the PIC or reset the emulator, the speaker will play a short tune.
; ; *************************************************************************** ; *** Bubble Software Parallax to PIC Source Converter. Copyright 1999. *** ; *** http://www.bubblesoftonline.com email: sales@picnpoke.com *** ; *************************************************************************** ; ; SOUND port, pin, frequency, duration ; Generates squarewave tones (notes) of the specified frequency and ; duration. This demonstration program shows how to use lookup tables to ; play tunes or effects from data stored in program memory. ; Device data and reset vector P = pic16c55 #include <16c55.inc> ; processor assembler definitions _CONFIG _xt_osc & _wdt_off & _protect_off reset start org 8 pin Res d'1' ; Pin number to use as output (0-7). freq Res d'1' ; Frequency of note. duratn Res d'1' ; Duration of note. f_copy Res d'1' ; Copy of freq variable. temp Res d'1' ; Variable to stretch duratn to 16 bits. index Res d'1' ; Index to tables of notes, lengths. org 0 ; Table to convert pin number (0-7) into bit mask (00000001b to 10000000b). Pinz ADDWF pcl RETLW d'1' RETLW d'2' RETLW d'4' RETLW d'8' RETLW d'16' RETLW d'32' RETLW d'64' RETLW d'128' ; Table of notes for the demonstration tune "Charge." Notes ADDWF pcl RETLW d'166' RETLW d'189' RETLW d'204' RETLW d'212' RETLW d'204' RETLW d'212' ; Table of note durations for the demonstration tune "Charge." Lengths ADDWF pcl RETLW d'52' RETLW d'52' RETLW d'52' RETLW d'82' RETLW d'45' RETLW d'190' start MOVLW d'0' ; Output for speaker. TRIS 5h CLRF temp ; Clear LSB of duratn counter. CLRF index ; Clear the note counter. start_loop MOVF index,w ; Look up note in table. CALL Notes MOVWF freq ; Store note in freq. MOVF index,w ; Look up length in table. CALL Lengths MOVWF duratn ; Store in duratn. MOVLW d'3' ; Select pin 3 of port MOVWF pin MOVLW d'0' ; RA for speaker output CALL Sound ; Play the sound. INCF index ; Advance to next sound in tables. MOVLW ~d'5' ; IF index <= 5, play note ADDWF index,w BTFSS status,c GOTO start_loop GOTO $ ; ELSE end. ; Upon entry, the desired speaker pin must already be set up as an output. ; The w register contains a number representing the output port (0-2) for ; RA through RC. Variable "pin" contains the pin number (0-7). The variables ; freq and duratn are the frequency and duration of the note. ; Upon exit, duratn will be cleared to 0, but freq will be intact. If you require the ; value of duratn for some other purpose, copy it before calling Sound. The ; variable pin and the file-select register (fsr) will also have be altered. Sound COMF duratn ; Convert to two's complement. INCF duratn MOVWF fsr ; Point to the port number. MOVLW 5h ; Add offset for port RA. ADDWF fsr MOVF pin,w ; Now look up pin number in CALL Pinz ; table and get bit mask. MOVWF pin ; Put the mask into pin. MOVF freq,w ; Use copy so freq value is saved. MOVWF f_copy BTFSC status,z ; If freq is 0, clear pin to CLRF pin ; produce a silent delay. Sound_loop MOVF pin,w ; f_copy=f_copy+1: IF f_copy<=255 INCFSZ f_copy ; THEN w=pin ELSE w=0 CLRW ; (makes a silent delay if freq = 0) XORWF indirect ; indirect=indirect XOR w MOVF f_copy,w ; If f_copy has rolled over to 0, BTFSC status,z ; reload it with the value of freq. MOVF freq,w MOVWF f_copy INCF temp ; Increment LSB of duratn counter. BTFSC status,z ; Overflow? Yes: Increment duratn and INCFSZ duratn ; if it overflows, return. Else loop. GOTO Sound_loop RETLW 0h end end
; SOUND port, pin, frequency, duration ; Generates squarewave tones (notes) of the specified frequency and ; duration. This demonstration program shows how to use lookup tables to ; play tunes or effects from data stored in program memory. org 8 pin ds 1 ; Pin number to use as output (0-7). freq ds 1 ; Frequency of note. duratn ds 1 ; Duration of note. f_copy ds 1 ; Copy of freq variable. temp ds 1 ; Variable to stretch duratn to 16 bits. index ds 1 ; Index to tables of notes, lengths. ; Device data and reset vector device pic16c55,xt_osc,wdt_off,protect_off reset start org 0 ; Table to convert pin number (0-7) into bit mask (00000001b to 10000000b). Pinz jmp pc+w retw 1,2,4,8,16,32,64,128 ; Table of notes for the demonstration tune "Charge." Notes jmp pc+w retw 166,189,204,212,204,212 ; Table of note durations for the demonstration tune "Charge." Lengths jmp pc+w retw 52,52,52,82,45,190 start mov !ra, #0 ; Output for speaker. clr temp ; Clear LSB of duratn counter. clr index ; Clear the note counter. :loop mov w,index ; Look up note in table. call Notes mov freq,w ; Store note in freq. mov w,index ; Look up length in table. call Lengths mov duratn,w ; Store in duratn. mov pin,#3 ; Select pin 3 of port mov w,#0 ; RA for speaker output call Sound ; Play the sound. inc index ; Advance to next sound in tables. cjbe index,#5,:loop ; IF index <= 5, play note jmp $ ; ELSE end. ; Upon entry, the desired speaker pin must already be set up as an output. ; The w register contains a number representing the output port (0-2) for ; RA through RC. Variable "pin" contains the pin number (0-7). The variables ; freq and duratn are the frequency and duration of the note. ; Upon exit, duratn will be cleared to 0, but freq will be intact. If you require the ; value of duratn for some other purpose, copy it before calling Sound. The ; variable pin and the file-select register (fsr) will also have be altered. Sound NEG duratn ; Convert to two's complement. mov fsr,w ; Point to the port number. add fsr,#RA ; Add offset for port RA. mov w,pin ; Now look up pin number in call Pinz ; table and get bit mask. mov pin,w ; Put the mask into pin. mov f_copy,freq ; Use copy so freq value is saved. snz ; If freq is 0, clear pin to clr pin ; produce a silent delay. :loop mov w,pin ; f_copy=f_copy+1: IF f_copy<=255 incsz f_copy ; THEN w=pin ELSE w=0 clr w ; (makes a silent delay if freq = 0) XOR indirect,w ; indirect=indirect XOR w mov w,f_copy ; If f_copy has rolled over to 0, snz ; reload it with the value of freq. mov w,freq mov f_copy,w inc temp ; Increment LSB of duratn counter. snz ; Overflow? Yes: Increment duratn and incsz duratn ; if it overflows, return. Else loop. jmp :loop ret
See also: